// Just the audio part


#ifndef LIGHTY_AUDIO_H
#define LIGHTY_AUDIO_H

#include <SDL_mixer.h>
#include <map>
#include <vector>
#include <string>
#include "Lighty_Debug.h"

#define NONAME ""
#define MUTE 0

const int MaxChannel=256;
const int MaxAudio=1000;

Mix_Chunk *SoundChunk[MaxAudio]={NULL};
Mix_Music *MusicChunk[MaxAudio]={NULL};

struct InfoAudio{

  std::string file;
  int loops;
  int fadeout;
  int time;
  double pos;
  std::vector<int> channel_list;
  long int pid; // Avoid parsing the container multiple times.
	
}; std::map<std::string, InfoAudio> AudioContainer;

class Lighty_AudioManager{
	
  public:
  
    class Load{
	
	  public:
	  
	    static int Sound(std::string name, std::string file, int loops=0, int fadeout=0, int time=-1){
		
	      InfoAudio Sequence={file,loops,fadeout,time,0,{MaxChannel+1},distance(AudioContainer.begin(),AudioContainer.find(name))};
	  
	      if(Sequence.pid==MaxAudio){
		
	        Lighty_PrintDebug("Audio error : Limit of preloaded sounds have been reach, can't load more !");
		  
	      }
	  
	      if(AudioContainer.insert(std::make_pair(name, Sequence)).second==false){
	  
            Lighty_PrintDebug("Audio error : Name's already taken !");
            return 1;
        
          }
			
          SoundChunk[AudioContainer[name].pid]=Mix_LoadWAV(AudioContainer[name].file.c_str());
          return 0;
			  
        }
	
	    static int Music(std::string name, std::string file, int loops=-1, int fadeout=0, double pos=0){
		
	      InfoAudio Sequence={file,loops,fadeout,0,pos,{MaxChannel+1},distance(AudioContainer.begin(),AudioContainer.find(name))};
	  
	 	  if(Sequence.pid==MaxAudio){
		
	        Lighty_PrintDebug("Audio error : Limit of preloaded musics have been reach, can't load more !");
		  
	  	  }
	  
	      if(AudioContainer.insert(std::make_pair(name, Sequence)).second==false){
	  
            Lighty_PrintDebug("Audio error : Name's already taken !");
        	return 1;
        
          }
			
          MusicChunk[AudioContainer[name].pid]=Mix_LoadMUS(AudioContainer[name].file.c_str());
          return 0;
			  
        }
	
    };
    
    class Unload{ // Free something while it's playing isn't safe...
	
      public:
  
        static void All(){
		
	      for(int i=0;i<MaxAudio;i++){
		  
	        if(MusicChunk[i]!=NULL){
			
		      Mix_FreeMusic(MusicChunk[i]);
		      MusicChunk[i]=NULL;
			
		    }
		
		    else if(SoundChunk[i]!=NULL){
			
		      Mix_FreeChunk(SoundChunk[i]);
		      SoundChunk[i]=NULL;
		 	
		    }
		  
	      }
	  
	      AudioContainer.clear();
		
	    }
  
        static int Sound(std::string name){
		
	      if(AudioContainer.find(name)==AudioContainer.end()){
		  
	        Lighty_PrintDebug("Audio error : Name not found, can't free the file !");
	        return 1;
		  
	      }
	  
	      Mix_FreeChunk(SoundChunk[AudioContainer[name].pid]);
	      SoundChunk[AudioContainer[name].pid]=NULL;
	      AudioContainer.erase(AudioContainer.find(name),AudioContainer.end());
	      return 0;
		
	    }
	
       	static int Music(std::string name){
	
	      if(AudioContainer.find(name)==AudioContainer.end()){
		  
	        Lighty_PrintDebug("Audio error : Name not found, can't free the file !");
	        return 1;
		  
	      }
	  
	      Mix_FreeMusic(MusicChunk[AudioContainer[name].pid]);
	      MusicChunk[AudioContainer[name].pid]=NULL;
	      AudioContainer.erase(AudioContainer.find(name),AudioContainer.end());
	      return 0;
		
	    }
	
    };
	
};

class Lighty_AudioPlayer{
	
  public:
  
    class Play{
		
	  public:
	  
	    static int Sound(std::string name){
	  
          if(AudioContainer.find(name)==AudioContainer.end()){
		  
	        Lighty_PrintDebug("Audio error : Name not found, can't play the sound !");
            return 1;
		  
	      }
			  
	      for(int i=0;i<MaxChannel;i++){ // Useless loops, can be fixed for less latency.
				
	        if(Mix_Playing(i)==0){
				
		      if(Mix_FadeInChannelTimed(i, SoundChunk[AudioContainer[name].pid], AudioContainer[name].loops, AudioContainer[name].fadeout, AudioContainer[name].time)==-1){
				
		        Lighty_PrintDebug("Audio error : The sound cannot be played for some reason !");
                return 1;
				
	          }
	      
	          if(AudioContainer[name].loops<0){
			
			    AudioContainer[name].channel_list.push_back(i);
			    			  
		      }
	      
	          return 0;
	            
	       }
	          
	     }
	  
	     return 1; // because the compiler is mad.
	        				
       }
	
       static int Music(std::string name){
		
	     if(AudioContainer.find(name)==AudioContainer.end()){
		  
	       Lighty_PrintDebug("Audio error : Name not found, can't play the sound !");
           return 1;
		  
	     }
			
	     if(Mix_FadeInMusicPos(MusicChunk[AudioContainer[name].pid], AudioContainer[name].loops, AudioContainer[name].fadeout, AudioContainer[name].pos)==-1){
  
           Lighty_PrintDebug("Audio error : The music couldnt be played !");
           return 1;
  
         }
          
         return 0;
			
	    }
		
	};
	
	class Pause{
		
	  public:
	  
	    static int Sound(std::string name){
			
		  if(AudioContainer.find(name)==AudioContainer.end()){
		  
	        Lighty_PrintDebug("Audio error : Name not found, can't pause the sound !");
            return 1;
		  
	      }
	      
	      if(AudioContainer[name].loops>0){
			  
			Lighty_PrintDebug("Audio error : A temporary sound can't be paused !");
		    return 1;
			  
		  }
			
		  for(unsigned long int i=1;i<AudioContainer[name].channel_list.size();i++){
			  
		    Mix_Pause(AudioContainer[name].channel_list[i]);
			  
		  }
		  
		  return 0;
			
		}
		
		static void Music(){
			
		  Mix_PauseMusic();
			
		}
		
	};
	
	class Resume{
		
	  public:
	  
	    static int Sound(std::string name){
			
		   if(AudioContainer.find(name)==AudioContainer.end()){
		  
	        Lighty_PrintDebug("Audio error : Name not found, can't resume the sound !");
            return 1;
		  
	       }
	       
	       if(AudioContainer[name].loops>0){
			  
			Lighty_PrintDebug("Audio error : A temporary sound can't be resumed !");
		    return 1;
			  
		   }
	       
	       for(unsigned long int i=1;i<AudioContainer[name].channel_list.size();i++){
			  
		     Mix_Resume(AudioContainer[name].channel_list[i]);
			  
		   }
		   
		   return 0;
			
		}
		
		static void Music(){
			
		  Mix_ResumeMusic();
			
		}
		
	};
	
	class Stop{
		
	  public:
	  
	    static int Sound(std::string name, int fadeout=0){
			
		  if(AudioContainer.find(name)==AudioContainer.end()){
		  
	        Lighty_PrintDebug("Audio error : Name not found, can't stop the sound !");
            return 1;
		  
	      }
	      
	      if(AudioContainer[name].loops>0){
			  
			Lighty_PrintDebug("Audio error : A temporary sound can't be stopped !");
		    return 1;
			  
		  }
	      
	      for(unsigned long int i=1;i<AudioContainer[name].channel_list.size();i++){
			  
		    Mix_FadeOutChannel(AudioContainer[name].channel_list[i], fadeout);
			  
		  }
		  
		  AudioContainer[name].channel_list.clear();
		  AudioContainer[name].channel_list.push_back(MaxChannel+1); // Workaround, fix a bad design. Could to many bugs without that.
		  return 0;
			
		}
		
		static void Music(int fadeout=0){
			
		  Mix_FadeOutMusic(fadeout);
			
		}
		
	};
	
};

class Lighty_AudioTweaks{

  public:
  
    class SetVolume{
		
	  public:
	  
	    static void Sound(int volume){ // 0-128.
			
		  Mix_Volume(-1,volume);
			
		}
		
		static void Music(int volume){ // 0-128.
			
		  Mix_VolumeMusic(volume);
			
		}
	
	};
	
	class Effect{
		
		public:
		
		  static int Unregister(std::string name){
			  
		    if(AudioContainer.find(name)==AudioContainer.end()){
		  
	          Lighty_PrintDebug("Audio error : Name not found, can't unregister effect !");
              return 1;
		  
	        }
	        
	        for(unsigned long int i=1;i<AudioContainer[name].channel_list.size();i++){
			  
		      Mix_UnregisterAllEffects(AudioContainer[name].channel_list[i]);
			  
		    }
		    
		    return 0;
			  
		  }
		  
		  static int SetPosition(std::string name, Sint16 angle=1, Uint8 distance=1){  // Put an angle and an distance of 0 remove all effects of the channel !
			 
		    if(AudioContainer.find(name)==AudioContainer.end()){
		  
	          Lighty_PrintDebug("Audio error : Name not found, can't apply an effect !");
              return 1;
		  
	        }
	        
	        for(unsigned long int i=1;i<AudioContainer[name].channel_list.size();i++){
			  
		      Mix_SetPosition(AudioContainer[name].channel_list[i], angle, distance);
			  
		    }
		    
		    return 0;
			  
		  }
		
    };
	
};


#endif
